package poker;
import java.util.*;

class Table 
{
	//private Player[] players;
	private ArrayList<Player> players;
	private ArrayList<Player> bidders;
	private List<String> botNames;
	private Deck deck;
	//zetony
	private int chips;
	//pula
	private int pot;
	//wpisowe
	private int buyIn;
	//aktualny zaklad
	private int currBid;
	//roznica miedzy zakladem gracza, a aktualnym zakladem
	private int diff;
	//tryb all-in
	private boolean allIn;
	private static final int MAX_PLAYERS = 4;
	Table(final int initsize, final int inithumansize, final String[] humanNames, final int chips, final int buyIn) throws PokerException 
	{
		if (initsize < 2 || initsize > MAX_PLAYERS) 
		{
			throw new PokerException("Incorrect number of players");
		}
		if (inithumansize > initsize) 
		{
			throw new PokerException("Too many human players");
		}
		this.players = new ArrayList<Player>(initsize);
		for (int i = 0; i < inithumansize; i++) 
		{
			players.add(new Human(humanNames[i]));
		}
		botNames = new LinkedList<String>(Arrays.asList("Sarge","Grunt","Phobos","Hunter","Anarki","Daemia","Major","Doom"));
		Random nameGen = new Random();
		for (int i = inithumansize; i < initsize; i++) 
		{
			int x = nameGen.nextInt(botNames.size()-1);
			players.add(new Bot("<BOT>" + botNames.get(x)));
			botNames.remove(x);
		}
		
		//przydzielenie graczom losowej kolejnosci		
        int player1, player2;
        Player help;
		Random generator = new Random();		
        for (short i=0; i<10; i++)
        {
        	player1 = generator.nextInt(players.size());
        	player2 = generator.nextInt(players.size());
            help = players.get(player2);
            players.set(player2, players.get(player1));
            players.set(player1, help);
        }
		this.chips = chips;
		this.buyIn = buyIn;
		currBid = 0;
		System.out.println("The game has begun!");
	}
	void play() 
	{
		
		dealChips();
		
		while(players.size() > 1)
		{		
			this.deck = new Deck();
			newBeginner();
			collectBuyIn();
			//lista graczy licytujacych
			bidders = new ArrayList<Player>(players);
			//zerowanie zakladow i trybu all-in
			for(int q = 0; q < bidders.size(); q++)
			{
				bidders.get(q).setBid(0);
				bidders.get(q).setAllIn(false);
			}
			allIn = false;
			//pierwsza licytacja
			bidding();	
			if(bidders.size() == 1)
			{
				results();
				continue;
			}
			
			dealCards();
			exchangePhase();
			//druga licytacja
			if(!allIn)
			{
				bidding();
			}
			
			
			if(players.size() == 0)
			{
				break;
			}
			revealCards();
			results();
			
		}
		endGame();
	}
	void newBeginner()
	{
		Player temp;
		for(int i = 0; i < (players.size() - 1); i++)
		{
			temp = players.get(i + 1);
			players.set(i + 1, players.get(i));
			players.set(i, temp);
		}
	}
	void collectBuyIn()
	{
		for (int i = 0; i < players.size(); i++)
		{
			if (players.get(i).getChips() < buyIn)
			{
				System.out.println("Player " + players.get(i).getName() + " has not enough chips to buy-in and leaves the game.");
				players.remove(i);
				i--;
			}
			else
			{
				System.out.println("Player: " + players.get(i).getName());
				String action = players.get(i).buyIn();
				if (action.equals("quit"))
				{
					System.out.println("Player " + players.get(i).getName() + " has left the game.");
					players.remove(i);
					i--;
				}
				else
				{
					System.out.println(players.get(i).getName()+ " buys in.");
					pot += players.get(i).playChips(buyIn);
				}
			}
		}
	}
				
	void dealChips()
	{
		for (int i = 0; i < players.size(); i++)
		{
			players.get(i).receiveChips(chips);
		}
		pot = 0;
	}
	void dealCards() 
	{
		if(bidders.size() > 1)
		{
			System.out.println("Dealing cards");
			for (int i = 0; i < players.size(); i++) 
			{
				players.get(i).receiveCards(deck);
			}
		}
	}
	void bidding()
	{
		System.out.println("Bidding");
		boolean biddingEqual = false;
		while(!biddingEqual && bidders.size() > 1)
		{
			for (int i = 0; i < bidders.size(); i++)
			{
				if(bidders.size() == 1)
				{
					break;
				}
				if(bidders.get(i).isAllIn())
				{
					continue;
				}
				String action = new String();
				while(true)
				{
					action = bidders.get(i).action(currBid, allIn);
					//check
					if(action.equals("check"))
					{
						if(bidders.get(i).getBid() >= currBid)
						{
							System.out.println(bidders.get(i).getName() + " checks");
							break;
						}
						System.out.println("Cannot check: your bid is too low");
					}
					//bet
					else if(action.equals("bet"))
					{
						if(currBid > 0)
						{
							System.out.println("Cannot bet: current bid is already higher than 0");
						}
						else if(bidders.get(i).getChips() < 1)
						{
							System.out.println("Cannot bet: you have no chips");
						}
						else
						{
							while(true)
							{
								int a = bidders.get(i).bet();
								if(bidders.get(i).getChips() < a)
								{
									System.out.println("You don't have that much chips");
								}
								else
								{
									pot += bidders.get(i).playChips(a);
									currBid = a;
									bidders.get(i).setBid(currBid);
									System.out.println(bidders.get(i).getName() + " bets " + currBid);
									break;
								}
							}
							break;
						}
					}
					//raise
					else if(action.equals("raise"))
					{
						if(bidders.get(i).getChips()<1)
						{
							System.out.println("Cannot raise: you have no chips");
						}
						else
						{
							while(true)
							{
								int b = bidders.get(i).raise();
								if(bidders.get(i).getChips() < b)
								{
									System.out.println("You don't have that much chips");
								}
								else
								{
									diff = currBid - bidders.get(i).getBid();
									pot += bidders.get(i).playChips(diff+b);
									currBid += b;
									bidders.get(i).setBid(currBid);
									System.out.println(bidders.get(i).getName() + " raises by " + b);
									break;
								}
							}
							break;
						}
					}
					//call
					else if(action.equals("call"))
					{
						diff = currBid - bidders.get(i).getBid();
						if(bidders.get(i).getChips() < diff)
						{
							System.out.println("Cannot call: you have not enough chips");
						}
						else if(bidders.get(i).getBid() >= currBid )
						{
							System.out.println(bidders.get(i).getName() + " checks");
							break;
						}
						else
						{
							pot += bidders.get(i).playChips(diff);
							bidders.get(i).setBid(currBid);
							System.out.println(bidders.get(i).getName() + " calls (" + currBid + ")");
							//spr czy call konczy aukcje
							
							boolean called = true;
							for(int index = 0; index < bidders.size(); index++)
							{
								if((bidders.get(index).getBid() != currBid) && !bidders.get(index).isAllIn())
								{
									called = false;
									break;
								}	
							}
							if(called)
							{
								i = bidders.size();
							}
							break;	
						}
					}
					//fold
					else if(action.equals("fold"))
					{					
						System.out.println(bidders.get(i).getName() + " folds");
						bidders.remove(i);
						i--;
						break;
					}
					//all-in
					else if(action.equals("all-in"))
					{
						int d = bidders.get(i).getChips();
						pot += bidders.get(i).playChips(d);
						bidders.get(i).setAllIn(true);
						System.out.println(bidders.get(i).getName() + " plays all-in (" + d + ")");
						allIn = true;
						int e = bidders.get(i).getBid();
						bidders.get(i).setBid(e + d);
						if(bidders.get(i).getBid() > currBid)
						{
							currBid = bidders.get(i).getBid();
						}
						break;
					}
					//wyjscie z gry
					else if(action.equals("quit"))
					{
						int q = 0;
						while(bidders.get(i) != players.get(q))
						{
							q++;
						}
						System.out.println(bidders.get(i).getName() + " has left the game");
						bidders.remove(i);
						players.remove(q);
						break;
					}
				}
			}
			//sprawdzanie, czy wszystkie zaklady sa rowne
			biddingEqual = true;
			for(int index = 0; index < bidders.size(); index++)
			{
				if((bidders.get(index).getBid() != currBid) && !bidders.get(index).isAllIn())
				{
					biddingEqual = false;
					break;
				}
			}
		}
	}
	void exchangePhase() 
	{
		if(bidders.size() > 1)
		{
			System.out.println("Exchange phase");
			for (int i = 0; i < bidders.size(); i++) 
			{
				System.out.println(players.get(i).getName() + "'s turn");
				int[] picks = players.get(i).picks();
				players.get(i).exchangeCards(picks, deck);
			}
			System.out.println("Cards exchanged");
		}
	}
	void revealCards()
	{
		for (int x = 0; x < bidders.size(); x++) 
		{
			System.out.println(bidders.get(x).getName() + ": ");
			bidders.get(x).getHand().display();
			System.out.print(bidders.get(x).getHand().toString());
		}
	}
	void results() 
	{
		int winners = 0;	
		int allInWinners = 0;
		int mainPot = 0;
		if(players.size() == 1)
		{
			players.get(0).receiveChips(pot);
			System.out.println(players.get(0).getName() + " won " + pot + " chips!");
			bidders.get(0).wins();
			pot = 0;
			return;
		}
		//wyniki przed rozdaniem
		if(bidders.get(0).getHand() == null)
		{			
			System.out.println(bidders.get(0).getName() + " won " + pot + " chips!!!");
			bidders.get(0).receiveChips(pot);
			bidders.get(0).wins();
			pot = 0;
		}
		//wyniki po rozdaniu
		if(bidders.get(0).getHand() != null)
		{
			Hand winningHand = null;
			winningHand = bidders.get(0).getHand();		
			for (int i = 1; i < bidders.size(); i++) 
			{
				if (bidders.get(i).getHand().compareTo(winningHand) > 0)
				{
					winningHand = bidders.get(i).getHand();
				}
			}
			for (int i = 0; i < bidders.size(); i++) 
			{
				if (bidders.get(i).getHand().compareTo(winningHand) == 0)
				{
					winners++;
					if(bidders.get(i).isAllIn())
					{
						allInWinners++;
					}
				}
			}
			
			while(winners > 0)
			{
				
				//wygrywa gracz bez all-in
				if (winners == 1 && allInWinners == 0) 
				{
					for (int i = 0; i < bidders.size(); i++) 
					{ 
						if (bidders.get(i).getHand().compareTo(winningHand) == 0)
						{
							System.out.println(bidders.get(i).getName() + " won " + pot + " chips!");
							bidders.get(i).receiveChips(pot);
							pot = 0;
							bidders.get(i).wins();
							winners--;
						}
					}
				}
				//remis kilku graczy; bez all-in
				else if(allInWinners == 0)
				{
					System.out.println("Players ");
					for (int i = 0; i < bidders.size(); i++) 
					{ 
						if (bidders.get(i).getHand().compareTo(winningHand) == 0)
						{
							winners--;
							pot -= currBid;
							bidders.get(i).receiveChips(currBid);
							if (winners > 0) 
							{
								System.out.print(bidders.get(i).getName() + ", ");
							} 
							else 
							{
								System.out.print(bidders.get(i).getName() + " won!");
							}
						}
					}
					System.out.println("");
				}
				//wygrywa jeden gracz z all-in
				else if((allInWinners == 1) && (winners == 1))
				{
					for( int i = 0; i < bidders.size(); i++)
					{
						if (bidders.get(i).getHand().compareTo(winningHand) == 0)
						{							
							mainPot = 0;
							for(int n = 0; n < players.size(); n++)
							{
								//zwyciezca z all-in bierze od kazdego gracza
								//tylko rownowartosc swojej stawki lub mniej
								if(players.get(n).getBid() >= bidders.get(i).getBid())
								{
									mainPot += bidders.get(i).getBid();
								}
								else
								{
									mainPot += players.get(n).getBid();
								}
								mainPot += buyIn;
							} 
							winners--;
							allInWinners--;
							pot -= mainPot;
							bidders.get(i).receiveChips(mainPot);
							System.out.println(bidders.get(i).getName() + " won " + mainPot + " chips! (All-in)");
							mainPot = 0;
							bidders.get(i).wins();
							bidders.remove(i);
						}
					}
				}
				//remis graczy z all-in
				else if(allInWinners > 1)
				{
					System.out.println("Players ");
					int tempBid = 0;
					for (int i = 0; i < bidders.size(); i++) 
					{ 
						if ((bidders.get(i).getHand().compareTo(winningHand) == 0) && (bidders.get(i).isAllIn()))
						{
							winners--;
							allInWinners--;
							tempBid = bidders.get(i).getBid();
							tempBid += buyIn;
							pot -= tempBid;
							bidders.get(i).receiveChips(tempBid);
							if (allInWinners > 0) 
							{
								System.out.print(bidders.get(i).getName() + ", ");
							} 
							else 
							{
								System.out.print(bidders.get(i).getName() + " won! (All-in)");
							}
							bidders.remove(i);
						}
					}
					System.out.println("");
				}			
			}
		}	
		for (int i = 0; i < players.size(); i++) 
		{
			players.get(i).discardHand();
		}
		deck.discardDeck();
		//pot = 0;
		currBid = 0;
		//pokazanie wynikow
		System.out.println("");
		System.out.println("Results");
		for(int i = 0; i < players.size(); i++)
		{
			System.out.println(players.get(i).getName() + ": " + players.get(i).getChips()
					+ " chips, " + players.get(i).getWins() + " wins");
		}
	}
	
	void endGame()
	{
		if(players.size() == 1)
		{		
			System.out.println(players.get(0).getName() + " won " + "with " + players.get(0).getChips() + " chips!");
		}
		
	}
}